﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using GemBlaster.UI;
using System.Collections.Generic;

namespace GemBlaster.Mechanics
{
    public class GameView : IUiView
    {
        IGameController Controller = null;

        // game layout constants
        int columns = 26;
        int rows = 14;
        double width = 771.635;
        double height = 410.45 - 5;
        double left = 15.171;
        double top = 96.274;

        // the game screen
        GameScreen _screen = null;

        // control displayed when game is over
        GameOver _gameOver = null;

        // x/y positions of each game cell
        Point[,] _playingGridLayout;

        // grid of gem references, one per game cell
        Gem[,] _playingGridSprites;

        // cache of Gem objects allows us to reuse them for slight performance advantage
        struct GemItem { internal bool InUse; internal Gem Gem; }
        GemItem[] _GemCache;

        public void Init(IGameController controller)
        {
            Controller = controller;
            Init();
        }

        public void Activate()
        {
            if (_screen == null)
            {
                _screen = new GameScreen();
                _screen.HighScore = SavedGameData.GetHighScore();
                _screen.Button1Click += DoNewGame;
                _screen.Button4Click += DoVisitWintellect;
                _screen.Button2Click += DoCheat;
                _screen.Button3Click += DoHint;
            }

            UpdateButtons();
            Controller.AddVisual(_screen);

            NewGame();
        }

        public void Deactivate()
        {
            for (int ix = 0; ix < _GemCache.Length; ix++)
            {
                GemItem item = _GemCache[ix];
                if (item.InUse)
                {
                    item.Gem.Reset();
                    item.InUse = false;
                    Controller.RemoveVisual(item.Gem);
                }
            }
            Controller.RemoveVisual(_screen);
        }

        // Shared delegate instances to provide a slight performance improvement
        MouseEventHandler _gemMouseEnter;
        EventHandler _gemMouseLeave;
        MouseEventHandler _gemMouseLeftButtonDown;
        EventHandler _gemMoveAnimationStarting;
        EventHandler _gemMoveAnimationComplete;
        EventHandler _gemCollapseAnimationStarting;
        EventHandler _gemCollapseAnimationComplete;

        void Init()
        {
            double gemMarginLeft = (width - (Gem.GemSize * columns)) / (columns + 1);
            double gemMarginTop = (height - (Gem.GemSize * rows)) / (rows + 1);

            double cellWidth = Gem.GemSize + gemMarginLeft;
            double cellHeight = Gem.GemSize + gemMarginTop;

            double rowLeftOffset = left + gemMarginLeft;
            double rowTopOffset = top + gemMarginTop;

            _playingGridLayout = new Point[columns, rows];
            for (int col = 0; col < columns; col++)
                for (int row = 0; row < rows; row++)
                {
                    double cellLeft = rowLeftOffset + (col * cellWidth);
                    double cellTop = rowTopOffset + (row * cellHeight);
                    _playingGridLayout[col, row].X = cellLeft;
                    _playingGridLayout[col, row].Y = cellTop;
                }

            // set up the shared event handlers (all gem instances will use same handler delegates)
            _gemMouseEnter = gem_MouseEnter;
            _gemMouseLeave = gem_MouseLeave;
            _gemMouseLeftButtonDown = gem_MouseLeftButtonDown;
            _gemMoveAnimationStarting = gem_MoveAnimationStarting;
            _gemMoveAnimationComplete = gem_MoveAnimationComplete;
            _gemCollapseAnimationStarting = gem_CollapseAnimationStarting;
            _gemCollapseAnimationComplete = gem_CollapseAnimationComplete;

            // fill the cache with unused gem objects
            _GemCache = new GemItem[columns * rows];
            for (int ix = 0; ix < _GemCache.Length; ix++)
            {
                _GemCache[ix].Gem = new Gem();
                _GemCache[ix].Gem.MouseEnter += _gemMouseEnter;
                _GemCache[ix].Gem.MouseLeave += _gemMouseLeave;
                _GemCache[ix].Gem.MouseLeftButtonDown += _gemMouseLeftButtonDown;
                _GemCache[ix].Gem.MoveAnimationStarting += _gemMoveAnimationStarting;
                _GemCache[ix].Gem.MoveAnimationComplete += _gemMoveAnimationComplete;
                _GemCache[ix].Gem.CollapseAnimationStarting += _gemCollapseAnimationStarting;
                _GemCache[ix].Gem.CollapseAnimationComplete += _gemCollapseAnimationComplete;
                _GemCache[ix].InUse = false;
            }

            _playingGridSprites = new Gem[columns, rows];
        }

        int _cheatsLeft = 5;
        int _hintsLeft = 5;

        int _unitScore = 5;
        int _comboBonus = 1;

        void DoNewGame(object sender, EventArgs e)
        {
            NewGame();
        }

        void DoVisitWintellect(object sender, EventArgs e)
        {
            System.Windows.Browser.HtmlPage.Navigate("http://www.wintellect.com/");
        }

        void DoCheat(object sender, EventArgs e)
        {
            if (_cheatsLeft > 0)
            {
                _cheatsLeft--;
                UpdateButtons();

                //Randomize the colors
                for (int ColIx = 0; ColIx < columns; ColIx++)
                    for (int RowIx = 0; RowIx < rows; RowIx++)
                        if (_playingGridSprites[ColIx, RowIx] != null)
                            _playingGridSprites[ColIx, RowIx].GemColor = RandomColor();
            }
        }

        void DoHint(object sender, EventArgs e)
        {
            if (_hintsLeft > 0)
            {
                _hintsLeft--;
                UpdateButtons();

                //find a set of adjacent gems and show the hint
                for (int colIx = 0; colIx < columns; colIx++)
                    for (int rowIx = 0; rowIx < rows; rowIx++)
                        if (_playingGridSprites[colIx, rowIx] != null)
                        {
                            CoordList NewSelection = new CoordList();
                            Coord CenterPoint = new Coord(colIx, rowIx, true, 0);
                            FindAdjacentItems(CenterPoint, NewSelection, CenterPoint);
                            if (NewSelection.Count > 1)
                            {
                                // found a potential selection
                                EstablishSelection(_playingGridSprites[colIx, rowIx]);
                                return;
                            }
                        }
            }
        }

        void UpdateButtons()
        {
            _screen.Button1Text = "New Game";
            _screen.Button2Text = String.Format("Cheat ({0})", _cheatsLeft);
            _screen.Button3Text = String.Format("Hint ({0})", _hintsLeft);
            _screen.Button4Text = "Wintellect";
        }

        void AccumulateScore(int gems)
        {
            int bonusPer = ((gems > 2) ? (gems - 2) : 0) * _comboBonus;
            int baseScore = _unitScore * gems;
            int bonusScore = ((gems > 2) ? (gems - 2) : 0) * bonusPer;
            int score = baseScore + bonusScore;
            _screen.CurrentScore += score;
        }

        void NewGame()
        {
            for (int ColIx = 0; ColIx < columns; ColIx++)
                for (int RowIx = 0; RowIx < rows; RowIx++)
                    if (_playingGridSprites[ColIx, RowIx] != null)
                    {
                        Controller.RemoveVisual(_playingGridSprites[ColIx, RowIx]);
                        _playingGridSprites[ColIx, RowIx] = null;
                    }
            for (int ix = 0; ix < _GemCache.Length; ix++)
                _GemCache[ix].InUse = false;
            if (_gameOver != null)
            {
                Controller.RemoveVisual(_gameOver);
                _gameOver = null;
            }

            TimeSpan baseDelay = new TimeSpan(0, 0, 0, 0, 10);
            TimeSpan runningDelay = new TimeSpan();

            runningDelay = new TimeSpan();
            for (int colIx = (columns / 2) - 1; colIx >= 0; colIx--)
                for (int rowIx = rows - 1; rowIx >= 0; rowIx--)
                {
                    // Create and animate a gem for the left side of screen
                    Gem LeftSprite = StartGem(_playingGridLayout[colIx, 0]);
                    _playingGridSprites[colIx, rowIx] = LeftSprite;
                    LeftSprite.MoveTo(_playingGridLayout[colIx, rowIx], runningDelay);

                    // Create and animate a gem for the right side of screen
                    int reflectedColIx = columns - colIx - 1;
                    Gem RightSprite = StartGem(_playingGridLayout[reflectedColIx, 0]);
                    _playingGridSprites[reflectedColIx, rowIx] = RightSprite;
                    RightSprite.MoveTo(_playingGridLayout[reflectedColIx, rowIx], runningDelay);

                    runningDelay = runningDelay.Add(baseDelay);
                }
            _screen.CurrentScore = 0;
            _cheatsLeft = 5;
            _hintsLeft = 5;
            UpdateButtons();
        }

        /// <summary>
        /// Locates the first unused gem and recycles it into service.
        /// </summary>
        /// <param name="startPos"></param>
        /// <returns></returns>
        private Gem StartGem(Point startPos)
        {
            Gem gem = null;
            for (int ix = 0; ix < _GemCache.Length; ix++)
                if (!_GemCache[ix].InUse)
                {
                    gem = _GemCache[ix].Gem;
                    _GemCache[ix].InUse = true;
                    break;
                }
            gem.GemColor = RandomColor();
            gem.Left = startPos.X;
            gem.Top = startPos.Y;
            Controller.AddVisual(gem);
            return gem;
        }

        int _activeMoveAnimations = 0;
        int _activeCollapseAnimations = 0;
        bool _pendingShifterCheck = false;
        bool _processingShifterStage = false;
        int _currentShifterStage = 0;

        bool SafeToAcceptInput()
        {
            return (_activeMoveAnimations == 0) && (_activeCollapseAnimations == 0) && !_pendingShifterCheck;
        }

        void gem_MoveAnimationComplete(object sender, EventArgs e)
        {
            _activeMoveAnimations--;
            CheckShifter();
        }

        void gem_MoveAnimationStarting(object sender, EventArgs e)
        {
            _activeMoveAnimations++;
        }

        void gem_CollapseAnimationComplete(object sender, EventArgs e)
        {
            // after collapsing a gem, remove it from the game matrix
            int col;
            int row;
            Gem gem = sender as Gem;
            FindGemCoordinates(gem, out col, out row);
            _playingGridSprites[col, row] = null;
            for (int ix = 0; ix < _GemCache.Length; ix++)
                if (_GemCache[ix].Gem == gem)
                {
                    _GemCache[ix].InUse = false;
                    break;
                }
            gem.Reset();
            Controller.RemoveVisual(gem);

            _activeCollapseAnimations--;
            CheckShifter();
        }

        void gem_CollapseAnimationStarting(object sender, EventArgs e)
        {
            _activeCollapseAnimations++;
        }

        void CheckShifter()
        {
            if (_processingShifterStage)
                return;
            if (_pendingShifterCheck && (_activeMoveAnimations == 0) && (_activeCollapseAnimations == 0))
            {
                // All current animations have completed, so now we can perform the next shifting operation
                switch (_currentShifterStage)
                {
                    case 1:
                        ShifterStage1();
                        break;
                    case 2:
                        ShifterStage2();
                        break;
                    case 3:
                        ShifterStage3();
                        break;
                }
            }
        }

        /// <summary>
        /// First stage of Shifter logic - all gems fall to the lowest position possible
        /// </summary>
        void ShifterStage1()
        {
            _processingShifterStage = true;
            TimeSpan baseDelay = new TimeSpan(0, 0, 0, 0, 10);
            TimeSpan runningDelay = TimeSpan.Zero;
            bool FirstRowFound = false;

            for (int colIx = columns - 1; colIx >= 0; colIx--)
                for (int rowIx = rows - 2; rowIx >= 0; rowIx--)
                {
                    if (FirstRowFound)
                        runningDelay = runningDelay + baseDelay;

                    if (_playingGridSprites[colIx, rowIx] != null)
                    {
                        int EmptySpacesBelow = 0;
                        for (int emptyIx = rowIx + 1; emptyIx < rows; emptyIx++)
                            if (_playingGridSprites[colIx, emptyIx] == null)
                                EmptySpacesBelow++;
                            else
                                break;
                        if (EmptySpacesBelow > 0)
                        {
                            // drop this gem down a few rows...
                            if (!FirstRowFound)
                                FirstRowFound = true;
                            _playingGridSprites[colIx, rowIx + EmptySpacesBelow] = _playingGridSprites[colIx, rowIx];
                            _playingGridSprites[colIx, rowIx] = null;
                            _playingGridSprites[colIx, rowIx + EmptySpacesBelow].MoveTo(_playingGridLayout[colIx, rowIx + EmptySpacesBelow], runningDelay);
                        }
                    }
                }
            _currentShifterStage = 2;
            _processingShifterStage = false;
            CheckShifter();
        }

        /// <summary>
        /// Second stage of Shifter logic - entire columns slide to replace any empty columns
        /// </summary>
        void ShifterStage2()
        {
            _processingShifterStage = true;
            TimeSpan baseDelay = new TimeSpan(0, 0, 0, 0, 10);
            TimeSpan runningLeftDelay = TimeSpan.Zero;
            TimeSpan runningRightDelay = TimeSpan.Zero;
            bool FirstLeftColFound = false;
            bool FirstRightColFound = false;

            for (int colIx = 0; colIx < (columns / 2); colIx++)
            {
                if (FirstLeftColFound)
                    runningLeftDelay = runningLeftDelay + baseDelay;

                if (_playingGridSprites[colIx, rows - 1] != null)
                {
                    int EmptySpacesToLeft = 0;
                    for (int emptyIx = colIx - 1; emptyIx >= 0; emptyIx--)
                        if (_playingGridSprites[emptyIx, rows - 1] == null)
                            EmptySpacesToLeft++;
                        else
                            break;
                    if (EmptySpacesToLeft > 0)
                    {
                        // slide this column over to the left...
                        if (!FirstLeftColFound)
                            FirstLeftColFound = true;
                        for (int rowIx = rows - 1; rowIx >= 0; rowIx--)
                        {
                            if (_playingGridSprites[colIx, rowIx] == null)
                                continue;
                            _playingGridSprites[colIx - EmptySpacesToLeft, rowIx] = _playingGridSprites[colIx, rowIx];
                            _playingGridSprites[colIx, rowIx] = null;
                            _playingGridSprites[colIx - EmptySpacesToLeft, rowIx].MoveTo(_playingGridLayout[colIx - EmptySpacesToLeft, rowIx], runningLeftDelay);
                        }
                    }
                }

                int reflectedColIx = columns - colIx - 1;
                if (FirstRightColFound)
                    runningRightDelay = runningRightDelay + baseDelay;

                if (_playingGridSprites[reflectedColIx, rows - 1] != null)
                {
                    int EmptySpacesToRight = 0;
                    for (int emptyIx = reflectedColIx + 1; emptyIx < columns; emptyIx++)
                        if (_playingGridSprites[emptyIx, rows - 1] == null)
                            EmptySpacesToRight++;
                        else
                            break;
                    if (EmptySpacesToRight > 0)
                    {
                        // slide this column over to the right...
                        if (!FirstRightColFound)
                            FirstRightColFound = true;
                        for (int rowIx = rows - 1; rowIx >= 0; rowIx--)
                        {
                            if (_playingGridSprites[reflectedColIx, rowIx] == null)
                                continue;
                            _playingGridSprites[reflectedColIx + EmptySpacesToRight, rowIx] = _playingGridSprites[reflectedColIx, rowIx];
                            _playingGridSprites[reflectedColIx, rowIx] = null;
                            _playingGridSprites[reflectedColIx + EmptySpacesToRight, rowIx].MoveTo(_playingGridLayout[reflectedColIx + EmptySpacesToRight, rowIx], runningRightDelay);
                        }
                    }
                }
            }
            _currentShifterStage = 3;
            _processingShifterStage = false;
            CheckShifter();
        }

        /// <summary>
        /// Third stage of Shifter logic - create new columns of gems in center
        /// </summary>
        void ShifterStage3()
        {
            _processingShifterStage = true;

            TimeSpan baseDelay = new TimeSpan(0, 0, 0, 0, 10);
            TimeSpan runningDelay = new TimeSpan();

            for (int colIx = (columns / 2) - 1; colIx >= 0; colIx--)
            {
                if (_playingGridSprites[colIx, rows - 1] == null)
                {
                    // need a new column of gems on left side
                    runningDelay = TimeSpan.Zero;
                    for (int rowIx = rows - 1; rowIx >= 0; rowIx--)
                    {
                        // Create and animate a gem for the left side of screen
                        Gem LeftSprite = StartGem(_playingGridLayout[colIx, 0]);
                        _playingGridSprites[colIx, rowIx] = LeftSprite;
                        LeftSprite.MoveTo(_playingGridLayout[colIx, rowIx], runningDelay);

                        runningDelay = runningDelay.Add(baseDelay);
                    }
                }
                int reflectedColIx = columns - colIx - 1;
                if (_playingGridSprites[reflectedColIx, rows - 1] == null)
                {
                    // need a new column of gems on right side
                    runningDelay = TimeSpan.Zero;
                    for (int rowIx = rows - 1; rowIx >= 0; rowIx--)
                    {
                        // Create and animate a gem for the right side of screen
                        Gem RightSprite = StartGem(_playingGridLayout[reflectedColIx, 0]);
                        _playingGridSprites[reflectedColIx, rowIx] = RightSprite;
                        RightSprite.MoveTo(_playingGridLayout[reflectedColIx, rowIx], runningDelay);

                        runningDelay = runningDelay.Add(baseDelay);
                    }
                }
            }

            _currentShifterStage = 0;
            _processingShifterStage = false;
            _pendingShifterCheck = false;

            CheckForEndOfGame();
        }

        void CheckForEndOfGame()
        {
            // If we can still cheat, then the game isnt over yet
            if (_cheatsLeft > 0)
                return;

            for (int colIx = 0; colIx < columns; colIx++)
                for (int rowIx = 0; rowIx < rows; rowIx++)
                    if (_playingGridSprites[colIx, rowIx] != null)
                    {
                        CoordList NewSelection = new CoordList();
                        Coord CenterPoint = new Coord(colIx, rowIx, true, 0);
                        FindAdjacentItems(CenterPoint, NewSelection, CenterPoint);
                        if (NewSelection.Count > 1)
                        {
                            // At least one possible combination left - game is not over yet
                            return;
                        }
                    }

            //Game Over
            if (_screen.CurrentScore > _screen.HighScore)
            {
                _screen.HighScore = _screen.CurrentScore;
                SavedGameData.SaveHighScore(_screen.HighScore);
            }
            _cheatsLeft = 0;
            _hintsLeft = 0;
            UpdateButtons();
            _gameOver = new GameOver();
            _gameOver.SetValue<double>(Canvas.TopProperty, (double)_screen.Mat.GetValue(Canvas.TopProperty));
            _gameOver.SetValue<double>(Canvas.LeftProperty, (double)_screen.Mat.GetValue(Canvas.LeftProperty));
            Controller.AddVisual(_gameOver);
        }

        TimeSpan MultiplyTimeSpan(TimeSpan value, int multiplier)
        {
            if (multiplier < 0)
                throw new ArgumentOutOfRangeException("multiplier", "Only positive TimeSpan multipliers are valid.");

            if (multiplier == 0)
                return TimeSpan.Zero;

            TimeSpan baseValue = value;
            for (int ix = 1; ix <= multiplier; ix++)
                value = value + baseValue;
            return value;
        }

        void gem_MouseLeftButtonDown(object sender, MouseEventArgs e)
        {
            if (!SafeToAcceptInput())
                return;
            if (_selection.Count > 1)
            {
                // block other clicks until the current shifter selection has completed
                _pendingShifterCheck = true;
                _currentShifterStage = 1;

                // add to the current score
                AccumulateScore(_selection.Count);

                TimeSpan baseDelay = new TimeSpan(0, 0, 0, 0, 100);

                foreach (Coord gemCoord in _selection)
                    _playingGridSprites[gemCoord.Col, gemCoord.Row].Collapse(MultiplyTimeSpan(baseDelay, gemCoord.DistanceFromCenter));
                _selection.Clear();

                CheckShifter();
            }
        }

        void gem_MouseLeave(object sender, EventArgs e)
        {
            if (!SafeToAcceptInput())
                return;
            CancelSelection();
        }

        void gem_MouseEnter(object sender, MouseEventArgs e)
        {
            if (!SafeToAcceptInput())
                return;
            EstablishSelection(sender as Gem);
        }

        bool FindGemCoordinates(Gem gem, out int col, out int row)
        {
            for (int colIx = 0; colIx < columns; colIx++)
            {
                for (int rowIx = 0; rowIx < rows; rowIx++)
                {
                    if (_playingGridSprites[colIx, rowIx] == gem)
                    {
                        col = colIx;
                        row = rowIx;
                        return true;
                    }
                }
            }
            col = -1;
            row = -1;
            return false;
        }

        void EstablishSelection(Gem centerGem)
        {
            int centerRoxIx = -1;
            int centerColIx = -1;
            if (FindGemCoordinates(centerGem, out centerColIx, out centerRoxIx))
            {
                CoordList NewSelection = new CoordList();
                Coord CenterPoint = new Coord(centerColIx, centerRoxIx, true, 0);
                FindAdjacentItems(CenterPoint, NewSelection, CenterPoint);

                if (NewSelection.Count > 1)
                {
                    CancelSelection();
                    _selection = NewSelection;
                    foreach (Coord gemCoord in _selection)
                        _playingGridSprites[gemCoord.Col, gemCoord.Row].StartHover();
                }
            }
        }

        bool InRange(Coord value)
        {
            return ((value.Col >= 0) && (value.Col < columns) && (value.Row >= 0) && (value.Row < rows));
        }

        bool QualifiesForSelection(Coord value, Coord compareTo, CoordList selectionList)
        {
            if (InRange(value))
                if (_playingGridSprites[value.Col, value.Row] != null)
                    if (_playingGridSprites[value.Col, value.Row].GemColor.Equals(_playingGridSprites[compareTo.Col, compareTo.Row].GemColor))
                        if (!selectionList.ContainsCoord(value))
                            return true;
            return false;
        }

        void FindAdjacentItems(Coord fromCenter, CoordList selectionList, Coord absoluteCenter)
        {
            if (!selectionList.ContainsCoord(fromCenter))
                selectionList.Add(fromCenter);

            Coord North = new Coord(fromCenter.Col, fromCenter.Row - 1, false, 0);
            Coord South = new Coord(fromCenter.Col, fromCenter.Row + 1, false, 0);
            Coord East = new Coord(fromCenter.Col + 1, fromCenter.Row, false, 0);
            Coord West = new Coord(fromCenter.Col - 1, fromCenter.Row, false, 0);

            North.DistanceFromCenter = Math.Max(Math.Abs(North.Col - absoluteCenter.Col), Math.Abs(North.Row - absoluteCenter.Row));
            South.DistanceFromCenter = Math.Max(Math.Abs(South.Col - absoluteCenter.Col), Math.Abs(South.Row - absoluteCenter.Row));
            East.DistanceFromCenter = Math.Max(Math.Abs(East.Col - absoluteCenter.Col), Math.Abs(East.Row - absoluteCenter.Row));
            West.DistanceFromCenter = Math.Max(Math.Abs(West.Col - absoluteCenter.Col), Math.Abs(West.Row - absoluteCenter.Row));

            if (QualifiesForSelection(North, fromCenter, selectionList))
                FindAdjacentItems(North, selectionList, absoluteCenter);

            if (QualifiesForSelection(South, fromCenter, selectionList))
                FindAdjacentItems(South, selectionList, absoluteCenter);

            if (QualifiesForSelection(East, fromCenter, selectionList))
                FindAdjacentItems(East, selectionList, absoluteCenter);

            if (QualifiesForSelection(West, fromCenter, selectionList))
                FindAdjacentItems(West, selectionList, absoluteCenter);
        }

        struct Coord
        {
            public int Row;
            public int Col;
            public bool CenterPoint;
            public int DistanceFromCenter;
            public Coord(int col, int row, bool centerPoint, int distanceFromCenter)
            {
                Row = row;
                Col = col;
                CenterPoint = centerPoint;
                DistanceFromCenter = distanceFromCenter;
            }
        }
        class CoordList : List<Coord>
        {
            public bool ContainsCoord(Coord value)
            {
                foreach (Coord item in this)
                    if ((item.Col == value.Col) && (item.Row == value.Row))
                        return true;
                return false;
            }
        }
        CoordList _selection = new CoordList();

        void CancelSelection()
        {
            foreach (Coord gemCoord in _selection)
                _playingGridSprites[gemCoord.Col, gemCoord.Row].StopHover();
            _selection.Clear();
        }


        Random Rnd = new Random();
        Color[] ColorSet = new Color[] { Colors.Blue, Colors.Green, Colors.Red, Colors.Cyan, Colors.Magenta };
        Color RandomColor()
        {
            int ix = Rnd.Next(0, ColorSet.Length);
            return ColorSet[ix];
        }
    }
}
